package com.alangyun.remind.repository.mapping;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

import com.alangyun.remind.error.MindException;
import com.alangyun.remind.repository.IDataset;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;

@XStreamAlias("alangyun-database-mapping")
public class DatabaseFile implements Serializable {
	private static final String XML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
	private static final String DATABASE_FILENAME = "database.xml";

	/** 数据集 * */
	@XStreamAlias("collections")
	private Map<String, DatasetFile> datasets;

	public Map<String, DatasetFile> getDatasets() {
		return datasets;
	}

	public void setDatasets(Map<String, DatasetFile> datasets) {
		this.datasets = datasets;
	}

	public DatasetFile get(String key) {
		return datasets.get(key);
	}

	public static DatabaseFile loadDatabase(File savePath) throws IOException {

		XStream xstream = new XStream();
		xstream.processAnnotations(DatabaseFile.class);
		xstream.allowTypes(new Class[] { DatabaseFile.class, 
				DatasetFile.class,
				DatasetHistory.class,
				DatasetHistory.HistoryFile.class
		});

		DatabaseFile db = null;

		File file = new File(savePath, DATABASE_FILENAME);
		if (file.exists()) {
			BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "utf-8"));
			db = (DatabaseFile) xstream.fromXML(reader);

			reader.close();
		}

		if (db == null)
			db = new DatabaseFile();
		if (db.datasets == null)
			db.datasets = new HashMap<>();

		return db;
	}

	public void saveDatabase(File savePath) throws IOException {

		// 然后保存数据库到文件
		XStream dbXStream = new XStream();
		dbXStream.processAnnotations(DatabaseFile.class);
		BufferedWriter dbWriter = new BufferedWriter(
				new OutputStreamWriter(new FileOutputStream(new File(savePath, DATABASE_FILENAME)), "utf-8"));
		dbWriter.write(XML + "\n" + dbXStream.toXML(this));
		dbWriter.close();
	}

	public String saveDataset(File savePath, String originName, IDataset data) throws IOException {
		// 构建待保存的数据文件名
		String dsFileName = joinDatasetFilename(data.getKey(), true);
		// 将添加的文件名添加到数据集记录文件中
		DatasetFile df = datasets.get(data.getKey());
		if (df == null) {
			df = DatasetFile.create(data.getKey());
			datasets.put(data.getKey(), df);
		}
		df.setLatest(dsFileName);
		df.getHistories().insert(dsFileName, originName);

		// 首先保存数据集到文件
		XStream dsXStream = new XStream();
		dsXStream.processAnnotations(data.getClass());
		BufferedWriter dsWriter = new BufferedWriter(
				new OutputStreamWriter(new FileOutputStream(new File(savePath, dsFileName)), "utf-8"));
		dsWriter.write(XML + "\n" + dsXStream.toXML(data));
		dsWriter.close();

		// 然后保存数据库配置到文件
		XStream dbXStream = new XStream();
		dbXStream.processAnnotations(DatabaseFile.class);
		BufferedWriter dbWriter = new BufferedWriter(
				new OutputStreamWriter(new FileOutputStream(new File(savePath, DATABASE_FILENAME)), "utf-8"));
		dbWriter.write(XML + "\n" + dbXStream.toXML(this));
		dbWriter.close();

		return dsFileName;
	}

	public <T> T openDataset(Class<? extends IDataset> mainClazz, File savePath, Class<?>... childClassMappers)
			throws IOException {
		DatasetFile df = datasets.get(classToKey(mainClazz));
		if (df == null)
			throw new MindException("未获取到预警数据，请确认是否已上传预警数据！");

		return openDataset(mainClazz, savePath, df.getLatest(), childClassMappers);
	}

	@SuppressWarnings("unchecked")
	public <T> T openDataset(Class<? extends IDataset> mainClazz, File savePath, String fileName,
			Class<?>... childClassMappers) throws IOException {

		File file = new File(savePath, fileName);
		if (!file.exists())
			throw new MindException("数据文件“" + file.getAbsolutePath() + "”不存在！");
		// 构造主类所包含的子类映射
		int length = 1 + (childClassMappers != null && childClassMappers.length > 0 ? childClassMappers.length : 0);
		Class<?>[] clazzArray = new Class[length];
		clazzArray[0] = mainClazz;
		if (length > 1) {
			int index = 1;
			for (Class<?> clzz : childClassMappers)
				clazzArray[index++] = clzz;
		}

		XStream xstream = new XStream();
		xstream.processAnnotations(mainClazz);
		xstream.allowTypes(clazzArray);
		BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "utf-8"));
		// 反序列化并返回
		T ret = (T) xstream.fromXML(reader);
		reader.close();

		return ret;
	}

	private String classToKey(Class<? extends IDataset> clazz) {
		try {
			return clazz.newInstance().getKey();
		} catch (Exception e) {
			throw new MindException("获取数据集键值失败", e);
		}
	}

	private String joinDatasetFilename(String key, boolean useTimestamp) {
		String ret = key;
		if (useTimestamp)
			ret = ret + "_" + System.currentTimeMillis();
		ret = ret + ".xml";

		return ret;
	}

}
